package com.jiashihui.schema_lgorithm.java.properties.util;

import java.io.*;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

public class SafeProperties extends Properties {

    private static final long serialVersionUID = 5011694856722313621L;

    private static final String keyValueSeparators = "=: \t\r\n\f";

    private static final String strictKeyValueSeparators = "=:";

    private static final String whiteSpaceChars = " \t\r\n\f";

    private PropertiesContext context = new PropertiesContext();

    public PropertiesContext getContext() {
        return context;
    }

    @Override
    public void load(InputStream inStream) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "8859_1"));
        while(true){
            // Get next line
            String line = in.readLine();
            // intact property/comment string
            String intactLine = line;
            if (line == null) {
                return;
            }
            if (line.length() > 0) {
                // Find start of key
                int len = line.length();
                int keyStart;
                for (keyStart = 0; keyStart < len; keyStart++) {
                    if (whiteSpaceChars.indexOf(line.charAt(keyStart)) == -1) {
                        break;
                    }
                }
                // Blank lines are ignored
                if (keyStart == len) {
                    continue;
                }
                // Continue lines that end in slashes if they are not comments
                char firstChar = line.charAt(keyStart);
                if ((firstChar != '#') || (firstChar != '!')) {
                    while (continueLine(line)) {
                        String nextLine = in.readLine();
                        intactLine = intactLine + "\n" + nextLine;
                        if (nextLine == null) {
                            nextLine = "";
                        }
                        String loppedLine = line.substring(0, len - 1);
                        // Advance beyond whitespace on new line
                        int startIndex;
                        for (startIndex = 0; startIndex < nextLine.length(); startIndex++) {
                            if (whiteSpaceChars.indexOf(nextLine.charAt(startIndex)) == -1) {
                                break;
                            }
                        }
                        nextLine = nextLine.substring(startIndex, nextLine.length());
                        line = new String(loppedLine + nextLine);
                        len = line.length();
                    }
                    // Find separation between key and value
                    int separatorIndex;
                    for (separatorIndex = keyStart; separatorIndex < len; separatorIndex++) {
                        char currentChar = line.charAt(separatorIndex);
                        if (currentChar == '\\') {
                            separatorIndex++;
                        } else if (keyValueSeparators.indexOf(currentChar) != -1) {
                            break;
                        }
                    }

                    // Skip over whitespace after key if any
                    int valueIndex;
                    for (valueIndex = separatorIndex; valueIndex < len; valueIndex++) {
                        if (whiteSpaceChars.indexOf(line.charAt(valueIndex)) == -1) {
                            break;
                        }
                    }
                    // Skip over one non whitespace key value separators if any
                    if (valueIndex < len && strictKeyValueSeparators.indexOf(line.charAt(valueIndex)) != -1) {
                        valueIndex++;
                    }
                    // Skip over white space after other separators if any
                    while (valueIndex < len) {
                        if (whiteSpaceChars.indexOf(line.charAt(valueIndex)) == -1) {
                            break;
                        }
                        valueIndex++;
                    }
                    String key = line.substring(keyStart, separatorIndex);
                    String value = (separatorIndex < len) ? line.substring(valueIndex, len) : "";
                    // Convert then store key and value
                    key = loadConvert(key);
                    value = loadConvert(value);
                    //memorize the property also with the whold string
                    put(key, value, intactLine);
                } else {
                    //memorize the comment string
                    context.addCommentLine(intactLine);
                }
            } else {
                //memorize the string even the string is empty
                context.addCommentLine(intactLine);
            }
        }
    }

    @Override
    public void store(OutputStream out, String header) throws IOException {
        synchronized (this) {
            BufferedWriter awriter;
            awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
            if (header != null) {
                writeln(awriter, "#" + header);
                writeln(awriter, "#" + new Date().toString());
            }
            List<Object> entrys = context.getCommentOrEntrys();
            for (Iterator<Object> iter = entrys.iterator(); iter.hasNext(); ) {
                Object obj = iter.next();
                if (obj.toString() != null) {
                    writeln(awriter, obj.toString());
                }
            }
            awriter.flush();
        }
    }

    @Override
    public synchronized Object put(Object key, Object value) {
        context.putOrUpdate(key.toString(), value.toString());
        return super.put(key, value);
    }

    public synchronized Object put(Object key, Object value, String line) {
        context.putOrUpdate(key.toString(), value.toString(), line);
        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        context.remove(key.toString());
        return super.remove(key);
    }

    /**
     * @param comment
     */
    public synchronized void addComment(String comment) {
        if (comment != null) {
            context.addCommentLine("#" + comment);
        }
    }

    private String loadConvert(String theString) {
        char aChar;
        int len = theString.length();
        StringBuffer outBuffer = new StringBuffer(len);

        for (int x = 0; x < len; ) {
            aChar = theString.charAt(x++);
            if (aChar == '\\') {
                aChar = theString.charAt(x++);
                if (aChar == 'u') {
                    // Read the xxxx
                    int value = 0;
                    for (int i = 0; i < 4; i++) {
                        aChar = theString.charAt(x++);
                        switch (aChar) {
                            case '0':
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                value = (value << 4) + aChar - '0';
                                break;
                            case 'a':
                            case 'b':
                            case 'c':
                            case 'd':
                            case 'e':
                            case 'f':
                                value = (value << 4) + 10 + aChar - 'a';
                                break;
                            case 'A':
                            case 'B':
                            case 'C':
                            case 'D':
                            case 'E':
                            case 'F':
                                value = (value << 4) + 10 + aChar - 'A';
                                break;
                            default:
                                throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
                        }
                    }
                    outBuffer.append((char) value);
                } else {
                    if (aChar == 't') {
                        outBuffer.append('\t');
                    } else if (aChar == 'r') {
                        outBuffer.append('\r');
                    } else if (aChar == 'n') {
                        outBuffer.append('\n');
                    } else if (aChar == 'f') {
                        outBuffer.append('\f');
                    } else {
                        outBuffer.append(aChar);
                    }
                }
            } else {
                outBuffer.append(aChar);
            }
        }
        return outBuffer.toString();
    }

    private static void writeln(BufferedWriter bw, String s) throws IOException {
        bw.write(s);
        bw.newLine();
    }

    private boolean continueLine(String line) {
        int slashCount = 0;
        int index = line.length() - 1;
        while ((index >= 0) && (line.charAt(index--) == '\\')) {
            slashCount++;
        }
        return (slashCount % 2 == 1);
    }
}